"""
书法评价系统主应用
提供Web界面，允许用户上传书法图片并获取评价结果
"""
from flask import Flask, request, jsonify, render_template, url_for, send_from_directory
import os
import json
import uuid
import tempfile
import traceback
import logging
from datetime import datetime
from werkzeug.utils import secure_filename
import numpy as np
import time
import cv2

# 导入自定义模块
from preprocessing import ImagePreprocessor
from feature_extraction import FeatureExtractor
from evaluation import CalligraphyEvaluator, STYLE_NAMES
from style_models import SUPPORTED_STYLES, STYLE_REGULAR
from style_classifier import StyleClassifier
from text_recognition import get_text_recognizer

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    filename='calligraphy_app.log',
    filemode='a'
)
logger = logging.getLogger('app')

# 创建Flask应用
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = os.path.join(os.getcwd(), 'uploads')
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 限制上传文件大小为16MB

# 确保上传目录存在
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)

# 允许的文件扩展名
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff'}

# 创建评价器实例
evaluator = CalligraphyEvaluator()

# 创建风格分类器实例
style_classifier = StyleClassifier()

# 全局OCR识别器
text_recognizer = None

def init_text_recognizer():
    """初始化OCR识别器"""
    global text_recognizer
    if text_recognizer is None:
        text_recognizer = get_text_recognizer()
    return text_recognizer

def allowed_file(filename):
    """检查文件是否有允许的扩展名"""
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/')
def index():
    """网站首页"""
    # 获取支持的所有书法风格
    styles = evaluator.get_supported_styles()
    return render_template('index.html', styles=styles, style_names=STYLE_NAMES)

@app.route('/evaluate', methods=['POST'])
def evaluate_calligraphy():
    """评估书法作品"""
    try:
        logger.info("接收到评估请求")
        start_time = time.time()
        
        # 获取图片文件
        if 'image' not in request.files:
            logger.error("没有上传文件")
            return jsonify({"error": "没有上传文件"}), 400
            
        image_file = request.files['image']
        if image_file.filename == '':
            logger.error("未选择文件")
            return jsonify({"error": "未选择文件"}), 400
            
        logger.info(f"上传文件名: {image_file.filename}")
        
        # 限制图像大小，避免处理过大的图像导致超时
        image_data = image_file.read()
        if len(image_data) > 10 * 1024 * 1024:  # 限制10MB
            logger.error(f"图像文件过大: {len(image_data)/1024/1024:.1f}MB")
            return jsonify({"error": "图像文件太大，请上传小于10MB的图像"}), 400
        
        # 自动识别风格现在始终启用
        auto_detect = True
        
        # 初始风格设为regular，但稍后会由自动识别结果覆盖
        style = 'regular'
        
        # 检查是否需要进行文本识别
        perform_ocr = request.form.get('perform_ocr', 'true').lower() == 'true'
        logger.info(f"文本识别设置: {perform_ocr}")
        
        # 保存上传的图片
        filename = secure_filename(image_file.filename)
        timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
        filename = f"{timestamp}_{filename}"
        image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        
        # 将读取的数据写入文件
        with open(image_path, 'wb') as f:
            f.write(image_data)
        logger.info(f"图片已保存至: {image_path}")
        
        # 处理图像
        logger.info("开始处理图像")
        preprocessor = ImagePreprocessor()
        image = preprocessor.load_image(image_path)
        
        # 设置图像处理的最大尺寸，避免处理过大的图像
        if max(image.shape[:2]) > 1500:
            # 等比例缩放
            scale = 1500 / max(image.shape[:2])
            new_width = int(image.shape[1] * scale)
            new_height = int(image.shape[0] * scale)
            image = cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_AREA)
            logger.info(f"图像已缩放至适当大小: {new_width}x{new_height}")
            
        processed_images = preprocessor.process_image(image)
        logger.info("图像处理完成")
        
        # 提取书法特征
        logger.info("开始提取特征")
        feature_extractor = FeatureExtractor()
        try:
            feature_extractor.load_processed_images(processed_images)
            features = feature_extractor.extract_all_features()
            logger.info("特征提取完成")
        except Exception as e:
            logger.error(f"特征提取失败: {str(e)}")
            return jsonify({"error": f"特征提取失败: {str(e)}"}), 500
        
        # 风格识别
        style_detection_results = None
        similar_masters = None
        
        try:
            logger.info("开始风格识别")
            style_detection_results = style_classifier.classify_style(features)
            # 使用识别出的最可能风格
            if style_detection_results and style_detection_results['confidence'] > 0:
                style = style_detection_results['best_style']
                logger.info(f"自动识别风格: {style_detection_results['best_style_name']}, 置信度: {style_detection_results['confidence']:.2f}")
        except Exception as e:
            logger.error(f"风格识别失败: {str(e)}")
            # 风格识别失败时使用默认风格regular
        
        # 寻找相似名家
        try:
            logger.info("开始名家匹配")
            similar_masters = style_classifier.find_similar_masters(features)
            logger.info(f"名家匹配结果: {similar_masters[:2] if similar_masters else 'None'}")
        except Exception as e:
            logger.error(f"名家匹配失败: {str(e)}")
        
        # 确保当前评价器使用正确的风格
        if evaluator.style_name != style:
            logger.info(f"切换评价器风格为: {style}")
            evaluator.change_style(style)
        
        # 评估书法作品
        try:
            logger.info("开始综合评价")
            evaluation_result = evaluator.evaluate_calligraphy(features)
            logger.info("综合评价完成")
            
            # 添加风格识别结果和名家匹配结果
            if style_detection_results:
                evaluation_result['style_detection'] = style_detection_results
            
            if similar_masters:
                evaluation_result['similar_masters'] = similar_masters
            
            # 如果启用了OCR识别且处理时间还未超过15秒，则执行OCR
            text_recognition_result = None
            elapsed_time = time.time() - start_time
            
            if perform_ocr and elapsed_time < 15:
                try:
                    logger.info("开始OCR文本识别")
                    # 初始化OCR识别器
                    recognizer = init_text_recognizer()
                    # 识别文本和出处
                    text_recognition_result = recognizer.recognize_text(image_path)
                    logger.info(f"OCR识别结果: {text_recognition_result.get('recognized_text', '无法识别')}")
                    
                    # 添加OCR识别结果
                    if text_recognition_result and text_recognition_result.get('success', False):
                        evaluation_result['text_recognition'] = {
                            'recognized_text': text_recognition_result.get('recognized_text', ''),
                            'source_info': text_recognition_result.get('source_info', {})
                        }
                except Exception as e:
                    logger.error(f"OCR识别失败: {str(e)}")
                    # OCR失败不影响整体评估，继续执行
            else:
                logger.warning(f"跳过OCR识别，当前处理已用时 {elapsed_time:.2f} 秒")
                
            # 确保所有数值都是基本类型，避免JSON序列化问题
            def ensure_serializable(data):
                """递归确保所有数据可被JSON序列化"""
                if isinstance(data, dict):
                    return {k: ensure_serializable(v) for k, v in data.items()}
                elif isinstance(data, list):
                    return [ensure_serializable(item) for item in data]
                elif isinstance(data, (np.int64, np.int32, np.int16, np.int8)):
                    return int(data)
                elif isinstance(data, (np.float64, np.float32, np.float16)):
                    return float(data)
                elif hasattr(data, 'tolist'):  # 处理numpy数组
                    return data.tolist()
                else:
                    return data
            
            # 对整个评估结果进行处理，确保所有数据可序列化
            evaluation_result = ensure_serializable(evaluation_result)
            
            # 记录总处理时间
            total_time = time.time() - start_time
            logger.info(f"评估请求总处理时间: {total_time:.2f}秒")
            evaluation_result['processing_time'] = round(total_time, 2)
                
            return jsonify(evaluation_result)
        except Exception as e:
            logger.error(f"评估失败: {str(e)}")
            import traceback
            logger.error(f"详细错误: {traceback.format_exc()}")
            return jsonify({"error": f"评估失败: {str(e)}"}), 500
    except Exception as e:
        logger.error(f"处理请求时出错: {str(e)}")
        import traceback
        logger.error(f"详细错误: {traceback.format_exc()}")
        return jsonify({"error": f"处理请求时出错: {str(e)}"}), 500

@app.route('/api/styles', methods=['GET'])
def get_styles():
    """获取支持的书法风格列表"""
    styles = evaluator.get_supported_styles()
    return jsonify({'styles': styles})

@app.route('/api/change_style', methods=['POST'])
def change_style():
    """切换评价器的书法风格"""
    data = request.json
    style_name = data.get('style')
    
    if not style_name:
        return jsonify({'error': 'No style specified'}), 400
    
    success = evaluator.change_style(style_name)
    
    if success:
        return jsonify({
            'success': True,
            'style': style_name,
            'style_name': STYLE_NAMES.get(style_name, style_name)
        })
    else:
        return jsonify({'error': f'Unsupported style: {style_name}'}), 400

@app.route('/feedback', methods=['POST'])
def submit_feedback():
    """处理用户对评价结果的反馈"""
    data = request.json
    user_feedback = data.get('feedback', {})
    
    # 更新评价器权重
    evaluator.update_weights(user_feedback)
    
    return jsonify({'success': True})

if __name__ == '__main__':
    app.run(debug=True) 